#pragma once
/*
日志落地模块
每一个类对应一个落地方向
落地方向:
1、标砖输出
2、指定文件
3、滚动文件(以大小滚动、以时间滚动...)
目前就这样...
扩展:网络服务器、数据库

设计思想：为了方便扩展
可以采用继承的设计层次
1、先抽象出一个抽象基类(该基类有一个接口专门实现具体落地的)
2、根据基类派生出具体落地方向...（如果要扩展的话，那么可以继承基类，重写落地接口）
3、采用工厂模式来创建落地方向,封装创建细节，让用户使用起来更爽！
*/
#include <iostream>
#include <string>
#include <fstream>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "util.hpp"
#include "sock.hpp"
namespace LoggerSpace
{
	class Sink
	{
	public:
		virtual ~Sink(){};
		// 具体落地接口
		virtual bool outLog(const char *, int) = 0;
	};
	// 落地方向是标准输出
	class StdoutSink : public Sink
	{
	public:
		bool outLog(const char *str, int len) override
		{
			std::cout.write(str, len);
			return !std::cout.fail();
		}
	};
	// 落地方向是一个具体文件
	class FileSink : public Sink
	{
	private:
		std::string _fileName;
		std::ofstream _of; // 利用文件流管理文件
	public:
		FileSink(const std::string &filename) : _fileName(filename)
		{
			// 先保文件所在目录存在
			LoggerSpace::util::createDirectory(LoggerSpace::util::getDirectory(_fileName));
			// 追加写的方式打开文件，文件不存在则创建
			_of.open(_fileName, std::ios::out | std::ios::app);
		}
		// 析构的时候关闭文件
		~FileSink() { _of.close(); }
		bool outLog(const char *str, int len) override
		{
			_of.write(str, len);
			return !_of.fail();
		}
	};

	// 落地方向是滚动文件(以大小为间隔)
	class RollSinkBySize : public Sink
	{
	private:
		std::string _baseName; // 滚动文件的基本名称
		std::ofstream _of;	   // 文件流来管理一个文件
		size_t _curSize;	   // 当前文件大小
		const size_t _MaxSize; // 单个文件总大小
		size_t _count;		   // 创建的文件的个数
		std::string createFileName()
		{
			std::string ret(_baseName);
			ret += "-v";
			ret += std::to_string(_count++);
			ret += ".log";
			return ret;
		}

	public:
		enum class FileSize : size_t
		{
			Small_Size = 512 * 1024,	   // 512k
			Middle_Size = 1 * 1024 * 1024, // 1M
			Big_Size = 10 * 1024 * 1024	   // 10M
		};
		RollSinkBySize(const std::string &baseName, FileSize MaxSzie = (FileSize::Small_Size))
			: _baseName(baseName), _MaxSize(static_cast<size_t>(MaxSzie)), _curSize(0), _count(1)
		{
			// 先保证文件所在路径存在
			LoggerSpace::util::createDirectory(LoggerSpace::util::getDirectory(_baseName));
			// 根据基本文件名+_v[count]的方式来形成新文件名
			std::string fileName = createFileName();
			_of.open(fileName, std::ios::out | std::ios::app);
		}
		bool outLog(const char *str, int len) override
		{
			if (_curSize + len > _MaxSize)
			{
				int newLen = _MaxSize - _curSize;
				_of.write(str, newLen);
				// 换文件继续写
				std::string ret = createFileName();
				_of.close();
				_of.open(ret, std::ios::out | std::ios::app);
				_of.write(str + newLen, len - newLen);
				_curSize = len - newLen;
			}
			else
			{
				_of.write(str, len);
				_curSize += len;
			}
			return !_of.fail();
		}
	};
	// 落地方向:滚动文件(通过过时间)
	class RollSinkByTime : public Sink
	{
	private:
		std::string _baseName; // 滚动文件的基本名称
		std::ofstream _of;	   // 文件流来管理一个文件
		size_t _timeGap;	   // 时间间隔
		time_t _createTime;	   // 文件创建时间
		std::string createFileName()
		{
			// 扩展名+xxxx-xx-xx_H:M:S.log
			time_t curTime = time(nullptr);
			struct tm tmp;
#ifdef _WIN32
			localtime_s(&tmp, &curTime);
#elif __linux__
			localtime_r(&curTime, &tmp);
#endif // _WIN32
			char buffer[128];
			snprintf(buffer, sizeof(buffer), "%04d%02d%02d%02d%02d%02d.log", tmp.tm_year + 1900, tmp.tm_mon + 1, tmp.tm_mday, tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
			std::string ret(_baseName);
			ret += buffer;
			return ret;
		}

	public:
		enum class TimeGap
		{
			Samll_Time = 10*60,
			Middle_Time = 3600,
			Big_Time = 24 * 3600
		};
		RollSinkByTime(const std::string &baseName, TimeGap tm = TimeGap::Samll_Time) : _baseName(baseName), _timeGap(static_cast<size_t>(tm))
		{
			// 先保证文件所在路径存在
			LoggerSpace::util::createDirectory(LoggerSpace::util::getDirectory(_baseName));
			std::string fileName = createFileName();
			_of.open(fileName, std::ios::out | std::ios::app);
		}
		bool outLog(const char *str, int len) override
		{
			// 大于设置时间间隔
			if (time(nullptr) - _createTime >= _timeGap)
			{
				std::string ret = createFileName();
				_of.close();
				_of.open(ret, std::ios::out | std::ios::app);
			}
			_of.write(str, len);
			return !_of.fail();
		}
	};

	// 扩展1:网络服务器
	class WebSink : public Sink
	{
	public:
		WebSink(const std::string &ip, uint16_t port) : _serverIp(ip), _serverPort(port)
		{
			_sk.Connect(_serverPort, _serverIp);
		}
		bool outLog(const char *str, int len) override
		{
			int pos = 0, n = 0;
			while (pos < len)
			{
				int n = send(_sk.GetSd(), str + pos, len - pos, 0);
				if (n > 0)
					pos += n;
				else
					return false;
			}
			return true;
		}

	private:
		std::uint16_t _serverPort;
		std::string _serverIp;
		Sock _sk;
	};
	// 采用工厂模式,封装落地对象创建细节
	class SinkFactory
	{
	public:
		template <class T, class... Args>
		static std::shared_ptr<T> createSink(Args... args)
		{
			return std::make_shared<T>(args...);
		}
	};
}